#ifndef CAN_HOST_H__
#define CAN_HOST_H__

#include "FreeRTOS.h"
#include "can_channel.h"
#include "message_buffer.h"
#include "module_base.h"
#include "semphr.h"

#define CAN_STD_WAIT_QUEUE_MAX (4)

#define CAN_RECV_SPEED_NORMAL 10 // ms
#define CAN_RECV_SPEED_HIGH 1    // ms

typedef void (*CanStdCmdCallback_t)(CanStdDataFrame_t &cmd);

typedef struct {
  Function_t function;
  CanStdCmdCallback_t cb;
} __packed MessageMap_t;

typedef struct {
  MAC_t mac;
  uint16_t length;
  uint8_t *data;
} CanExtCmd_t;

typedef struct {
  func_id_t id;
  uint8_t length;
  uint8_t *data;
} CanStdFuncCmd_t;

typedef struct {
  message_id_t id;
  uint8_t length;
  uint8_t *data;
} CanStdMesgCmd_t;

typedef struct {
  message_id_t message; /* command we are waiting for its ack */
  MessageBufferHandle_t queue;
} CanStdWaitNode_t;

typedef struct {
  // MAC_t     mac;   /* MAC ID of module which we are waiting for */
  uint16_t cmd; /* command we are waiting for its ack */
  MessageBufferHandle_t queue;
} CanExtWaitNode_t;

typedef enum {
  RECEIVER_SPEED_NORMAL,
  RECEIVER_SPEED_HIGH,
} RECEIVER_SPEED_E;

class CanHost {
public:
  ErrCode Init();

  ErrCode SendStdCmd(CanStdMesgCmd_t &message);
  ErrCode SendStdCmd(CanStdFuncCmd_t &function, uint8_t sub_index = 0);
  ErrCode SendStdCmdSync(CanStdFuncCmd_t &function, uint32_t timeout_ms = 0,
                         uint8_t retry = 1, uint8_t sub_index = 0);

  ErrCode SendExtCmd(CanExtCmd_t &cmd);
  ErrCode SendExtCmdSync(CanExtCmd_t &cmd, uint32_t timeout_ms = 0,
                         uint8_t retry = 1);
  ErrCode WaitExtCmdAck(CanExtCmd_t &cmd, uint32_t timeout_ms = 0,
                        uint8_t retry = 1);

  void SendHeartbeat();
  void SendEmergencyStop();

  void ReceiveHandler(void *parameter);
  void EventHandler(void *parameter);

  ErrCode UpgradeModules(uint32_t fw_addr, uint32_t length);

  message_id_t RegisterFunction(const Function_t &function,
                                CanStdCmdCallback_t callback);

  bool IrqCallback(CanStdDataFrame_t &frame);

  ErrCode BindMessageID(CanExtCmd_t &cmd, message_id_t *msg_buffer);
  void ShowModuleVersion(MAC_t mac);
  void SetReceiverSpeed(RECEIVER_SPEED_E speed);
  uint32_t mac(uint8_t index) {
    if (index < total_mac_)
      return mac_[index].val;
    else
      return MODULE_MAC_ID_INVALID;
  }

private:
  message_id_t GetMessageID(func_id_t function_id, uint8_t sub_index = 0);
  ErrCode BindMessageID(MAC_t &mac, uint8_t mac_index);

  ErrCode InitModules(MAC_t &mac);
  ErrCode InitDynamicModule(MAC_t &mac, uint8_t mac_index);

  ErrCode AssignMessageRegion();

  ErrCode HandleExtCmd(uint8_t *cmd, uint16_t length);

private:
  // command queue from ReceiveHandler() to EventHandler for standard command
  MessageBufferHandle_t std_cmd_q_;
  CanStdWaitNode_t std_wait_q_[CAN_STD_WAIT_QUEUE_MAX];
  xSemaphoreHandle std_wait_lock_;

  // parameters for extended command
  ProtocolSSTP proto_sstp_;         // SSTP protocol instance
  uint8_t *parser_buffer_;          // buffer for parser of SSTP protocol
  uint8_t *package_buffer_;         // buffer for packager of SSTP protocol
  MessageBufferHandle_t ext_cmd_q_; // command queue from ReceiveHandler() to
                                    // EventHandler for extend command
  CanExtWaitNode_t ext_wait_q_;
  xSemaphoreHandle ext_wait_lock_;
  uint8_t receiver_speed_ = CAN_RECV_SPEED_NORMAL;

  // map for message id and function id
  MessageMap_t map_message_function_[MODULE_SUPPORT_MESSAGE_ID_MAX];
  uint16_t total_message_id_;

  // in the second dimension, first element indicates begining we assign for
  // this priority second element indicates counts which has been used of this
  // priority
  uint16_t message_region_[MODULE_FUNC_PRIORITY_MAX][2];

  MAC_t mac_[MODULE_SUPPORT_CONNECTED_MAX];
  uint8_t total_mac_;
};

extern CanHost canhost;

#endif